/******************************************************************************
 * @file   :  item11.cpp
 * @brief  :  Understand the legitimate uses of custom allocators
 * Created :  09/12/2010 01:50:06 PM
 * Revision:  none
 * Compiler:  gcc -Wall -Wextra item11.cpp
 *
 * @author :  coanor, coanor@gmail.com
 ******************************************************************************/

void *malloc_shared(size_t bytes_needed)
{
	return (new char[bytes_needed]);
}
void free_shared(void *ptr)
{
	delete ptr;
}

template<typename T>
class shared_memory_allocator {
public:
	pointer allocate(size_type num_objects, const void *locality_hint = 0)
	{
		return static_cast<pointer>(malloc_shared(num_objects * sizeof(T)));
	}

	void deallocate(pointer ptr_to_memory, size_type num_objects)
	{
		free_shared(ptr_to_memory);
	}

	//...
};

// convenience typedef
typedef vector<double, shared_memory_allocator<double> > shared_double_vec;

namespace first_example {
	/* v is using a shared_memory_allocator, so the memory v allocates to hold
	 * its elements will come from shared memory, v itself, however, include all
	 * its data members, will almost certainly NOT be placed in shared memory, v
	 * is just a normal "stack-based object", so it will be located in whatever
	 * memory the OS uses for all normal stack-based objects, that's ALMOST NEVER
	 * shared memory.
	 */	
	shared_double_vec v;

	/* To put both v's content and v itself into shared memory, you'd have to do
	 * something like this:
	 *
	 * Allocate enough shared memory to hold a shared_double_vec object */
	void *p_vector_memory = malloc_shared(sizeof(shared_double_vec));

	/* use "placement new" to create a shared_double_vec object in the memory */
	shared_double_vec *pv = new(p_vector_memory)shared_double_vec;

	/*...*/

	pv->~shared_double_vec();	  /* destroy the object in the shared memory */
	free_shared(p_vector_memory); /* deallocate the initial chunck of shared memory */

}

namespace second_example {
	class Heap1 {
		//...
		static void *alloc(size_t num_bytes, const void *memory_block_to_be_near);
		static void dealloc(void *ptr);
		//...
	};

	class Heap2 {
		//...
		static void *alloc(size_t num_bytes, const void *memory_block_to_be_near);
		static void dealloc(void *ptr);
		//...

	};

	template<typename T, typename Heap>
	special_heap_allocator {
	public:
		pointer allocate(size_t num_objects, const void *locality_hint = 0)
		{
			return static_cast<pointer>(Heap::alloc(num_objects * sizeof(T), locality_hint));
		}

		void deallocate(pointer ptr_to_memory, size_type num_objects)
		{
			Heap::dealloc(ptr_to_memory);
		}
		//...
	};

	/* use special_heap_allocator to cluster containers' element together */
	/* put both v's and s's elements in Heap1 */
	vector<int, special_heap_allocator<int, Heap1> > v;
	set<int, special_heap_allocator<int Heap1> > s;

	/* put both L's and m's elements in Heap2 */
	list<widget, special_heap_allocator<widget, Heap2> > L;
	map<int, string, less<int>, special_heap_allocator<pair<const int, string>, Heap2> > m;
}

void main()
{
	return 0;
}
